<html lang="en-US">

<head>
    
<title>Go 应用程序是怎么运行起来的？ - 花落雨忧</title>

<meta property="og:title" content="Go 应用程序是怎么运行起来的？ - 花落雨忧">



    



    
    <meta property="description" content="自古应用程序均从 Hello World 开始，你我所写的 Go 语言亦然：
import &amp;#34;fmt&amp;#34; func main() { fmt.Println(&amp;#34;hello world.&amp;#34;) } 这段程序的输出结果为 hello world.，就是这么的简单又直接。但这时候又不禁思考了起来，这个 hello world. 是怎么输出来，经历了什么过程。
真是非常的好奇， &amp;hellip;">
    <meta property="og:description" content="自古应用程序均从 Hello World 开始，你我所写的 Go 语言亦然：
import &amp;#34;fmt&amp;#34; func main() { fmt.Println(&amp;#34;hello world.&amp;#34;) } 这段程序的输出结果为 hello world.，就是这么的简单又直接。但这时候又不禁思考了起来，这个 hello world. 是怎么输出来，经历了什么过程。
真是非常的好奇， &amp;hellip;">
    






<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">

<link rel="shortcut icon" href="https://www.lican.asia/logo/logo.png" type="image/x-icon" />



<link rel="stylesheet" href="/css/style.min.css" />

<link rel="stylesheet" href="/css/reset.min.css" />




<script src="https://www.lican.asia/js/highlight.min.js"></script>

<script>
hljs.configure({ ignoreUnescapedHTML: true })
hljs.highlightAll();
</script>


<script src="https://www.lican.asia/js/jquery.min.js"></script>




<link href="https://www.lican.asia/css/hugo-code.min.css" rel="stylesheet" />



    <style>
        .post-content img {
            max-width: 400px;
        }
    </style>
</head>

<body id="period" class="home blog">
    <a class="skip-content" href="#main">Press "Enter" to skip to content</a>
    <div id="overflow-container" class="overflow-container">
        <header class="site-header" id="site-header" role="banner">
    <div class="max-width">
        <div id="title-container" class="title-container">
            <div id="site-title" class="site-title"><a href="/">花落雨忧</a></div>
            <p class="tagline">思所及 力所行 方为真.</p>
        </div>
        
        <div id="menu-primary-container" class="menu-primary-container">
            <div class="icon-container">
            </div>
            <div id="menu-primary" class="menu-container menu-primary" role="navigation">
                <nav class="menu">
                    <ul id="menu-primary-items" class="menu-primary-items">
                        
                        
                        <li id="menu-item-0"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/posts" aria-current="page" tabindex="0">首页</a></li>
                        
                        <li id="menu-item-1"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/tech/" aria-current="page" tabindex="1">技术文档</a></li>
                        
                        <li id="menu-item-2"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/article/" aria-current="page" tabindex="2">文章</a></li>
                        
                        <li id="menu-item-3"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/project/" aria-current="page" tabindex="3">项目</a></li>
                        
                        <li id="menu-item-4"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/about/" aria-current="page" tabindex="4">关于</a></li>
                        
                    </ul>
                </nav>
            </div>
        </div>
    </div>
</header>

        <div id="primary-container" class="primary-container">
            <div class="max-width">
                <section id="main" class="main" role="main">
                    <div id="loop-container" class="loop-container">
                        <div
                            class="post type-post status-publish format-standard hentry entry">
                            <article>
                                <div class="post-container">
                                    <div class="post-header">
                                        <h2 class="post-title">
                                            <a href="/posts/posts/go/go-bootstrap/">Go 应用程序是怎么运行起来的？</a>
                                        </h2>
                                        
                                        <div class="post-byline">Published on
                                            <a class="date" href="javascript:;">2020/10/08</a>
                                            
                                            
                                            
                                            
                                            
                                        
                                    </div>
                                    <div class="post-content">
                                        <p>自古应用程序均从 Hello World 开始，你我所写的 Go 语言亦然：</p>
<pre tabindex="0"><code>import &#34;fmt&#34;

func main() {
	fmt.Println(&#34;hello world.&#34;)
}
</code></pre><p>这段程序的输出结果为 <code>hello world.</code>，就是这么的简单又直接。但这时候又不禁思考了起来，这个 <code>hello world.</code> 是怎么输出来，经历了什么过程。</p>
<p>真是非常的好奇，今天我们就通过这篇文章来一探究竟。</p>
<h2 id="引导阶段">引导阶段</h2>
<h3 id="查找入口">查找入口</h3>
<p>开始剖析之路，首先编译上文提到的示例程序：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ GOFLAGS<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;-ldflags=-compressdwarf=false&#34;</span> go build 
</span></span></code></pre></div><p>在命令中指定了 GOFLAGS 参数，这是因为在 Go1.11 起，为了减少二进制文件大小，调试信息会被压缩。导致在 MacOS 上使用 gdb 时无法理解压缩的 DWARF 的含义是什么（而我恰恰就是用的 MacOS）。</p>
<p>因此需要在本次调试中将其关闭，再使用 gdb 进行调试，以此达到观察的目的：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ gdb awesomeProject 
</span></span><span style="display:flex;"><span><span style="color:#f92672">(</span>gdb<span style="color:#f92672">)</span> info files
</span></span><span style="display:flex;"><span>Symbols from <span style="color:#e6db74">&#34;/Users/eddycjy/go-application/awesomeProject/awesomeProject&#34;</span>.
</span></span><span style="display:flex;"><span>Local exec file:
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">`</span>/Users/eddycjy/go-application/awesomeProject/awesomeProject<span style="color:#960050;background-color:#1e0010">&#39;</span>, file type mach-o-x86-64.
</span></span><span style="display:flex;"><span>	Entry point: 0x1063c80
</span></span><span style="display:flex;"><span>	0x0000000001001000 - 0x00000000010a6aca is .text
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span><span style="color:#f92672">(</span>gdb<span style="color:#f92672">)</span> b *0x1063c80
</span></span><span style="display:flex;"><span>Breakpoint <span style="color:#ae81ff">1</span> at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.
</span></span></code></pre></div><p>通过 Entry point 的调试，可看到真正的程序入口在 runtime 包中，不同的计算机架构指向不同，例如：MacOS 在 <code>src/runtime/rt0_darwin_amd64.s</code>，Linux 在 <code>src/runtime/rt0_linux_amd64.s</code>。</p>
<pre tabindex="0"><code>Breakpoint 1 at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.
</code></pre><p>其最终指向了 rt0_darwin_amd64.s 文件，这个文件名称非常的直观，rt0 代表 runtime0 的缩写，指代运行时的创世，超级奶爸；darwin 代表目标操作系统（GOOS），amd64 代表目标操作系统架构（GOHOSTARCH）。</p>
<p>同时 Go 语言还支持更多的目标系统架构，例如：AMD64、AMR、MIPS、WASM 等：</p>
<p><img src="https://image.eddycjy.com/981720dfbce750bec26fc394e97d9ff7.jpg" alt="image"></p>
<p>若有兴趣可到 <code>src/runtime</code> 目录下进一步查看。</p>
<h3 id="入口方法">入口方法</h3>
<p>在 rt0_linux_amd64.s 文件中，可发现 <code>_rt0_amd64_darwin</code> JMP 跳转到了 <code>_rt0_amd64</code> 方法：</p>
<pre tabindex="0"><code>TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
	JMP	_rt0_amd64(SB)
...
</code></pre><p>紧接着又跳转到 <code>runtime·rt0_go</code> 方法：</p>
<pre tabindex="0"><code>TEXT _rt0_amd64(SB),NOSPLIT,$-8
	MOVQ	0(SP), DI	// argc
	LEAQ	8(SP), SI	// argv
	JMP	runtime·rt0_go(SB)
</code></pre><p>该方法将程序输入的 argc 和 argv 从内存移动到寄存器中。栈指针（SP）的前两个值分别是 argc 和 argv，其对应参数的数量和具体各参数的值。</p>
<h3 id="开启主线">开启主线</h3>
<p>程序参数准备就绪后，正式初始化的方法落在 <code>runtime·rt0_go</code> 方法中：</p>
<pre tabindex="0"><code>TEXT runtime·rt0_go(SB),NOSPLIT,$0
	...
	CALL	runtime·check(SB)
	MOVL	16(SP), AX		// copy argc
	MOVL	AX, 0(SP)
	MOVQ	24(SP), AX		// copy argv
	MOVQ	AX, 8(SP)
	CALL	runtime·args(SB)
	CALL	runtime·osinit(SB)
	CALL	runtime·schedinit(SB)

	// create a new goroutine to start program
	MOVQ	$runtime·mainPC(SB), AX		// entry
	PUSHQ	AX
	PUSHQ	$0			// arg size
	CALL	runtime·newproc(SB)
	POPQ	AX
	POPQ	AX

	// start this M
	CALL	runtime·mstart(SB)
	...
</code></pre><ul>
<li>
<p>runtime.check：运行时类型检查，主要是校验编译器的翻译工作是否正确，是否有 “坑”。基本代码均为检查 <code>int8</code> 在 <code>unsafe.Sizeof</code> 方法下是否等于 1 这类动作。</p>
</li>
<li>
<p>runtime.args：系统参数传递，主要是将系统参数转换传递给程序使用。</p>
</li>
<li>
<p>runtime.osinit：系统基本参数设置，主要是获取 CPU 核心数和内存物理页大小。</p>
</li>
<li>
<p>runtime.schedinit：进行各种运行时组件的初始化，包含调度器、内存分配器、堆、栈、GC 等一大堆初始化工作。是后续的重点关注对象。</p>
</li>
<li>
<p>runtime·main：主要工作是运行 main goroutine，虽然在<code>runtime·rt0_go</code> 中指向的是<code>$runtime·mainPC</code> ，但实质指向的是 <code>runtime.main</code>。</p>
</li>
<li>
<p>runtime.newproc：创建一个新的 goroutine 将其放入 g 的等待运行队列中去。且绑定 <code>runtime.main</code> 方法，也就是应用程序中的入口 main 方法。</p>
</li>
<li>
<p>runtime.mstart：调度器开始进行循环调度。</p>
</li>
</ul>
<p>在 <code>runtime·rt0_go</code> 方法中，其主要是完成各类运行时的检查，系统参数设置和获取，并进行大量的 Go 基础组件初始化。初始化完毕后进行 main goroutine 的运行，并放入等待队列（GMP），最后调度器开始进行循环调度。</p>
<h2 id="总结">总结</h2>
<p>根据上述源码剖析，可以得出如下 Go 应用程序引导的流程图：</p>
<p><img src="https://image.eddycjy.com/057c1ccb06c16e8c5f38ff5800e3fa63.jpg" alt="image"></p>
<p>在 Go 语言中，实际的运行入口并不是用户日常所写的 <code>main func</code>，更不是 <code>runtime.main</code> 方法，而是从 <code>rt0_*_amd64.s</code> 开始，最终再一路 JMP 到 <code>runtime·rt0_go</code> 里去，再在该方法里完成一系列 Go 自身所需要完成的绝大部分初始化动作。</p>
<p>其中包括运行时类型检查、系统参数传递、CPU 核数获取及设置、运行时组件的初始化（调度器、内存分配器、堆、栈、GC 等）、运行 main goroutine 和相应的 GMP 等大量缺省行为，还会涉及到调度器相关的大量知识。</p>
<p>后续将会继续剖析将进一步剖析 <code>runtime·rt0_go</code> 里的爱与恨，尤其像是 <code>runtime.main</code>、<code>runtime.schedinit</code> 等调度方法，都有非常大的学习价值，有兴趣的小伙伴可以持续关注。</p>


                                        
                                        
                                        
                                        <div class="rp4wp-related-posts">
                                            <h3>相关文章</h3>
                                            <ul>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.asia/posts/posts/go-programming-tour-book/">新书《Go语言编程之旅：一起用Go做项目》出版啦！</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.asia/posts/posts/why-container-memory-exceed2/">为什么容器内存占用居高不下，频频 OOM（续）</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.asia/posts/posts/why-container-memory-exceed/">为什么容器内存占用居高不下，频频 OOM</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.asia/posts/posts/go/go-moduels/2020-02-28-go-modules/">Go Modules 终极入门</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.asia/posts/posts/go/go-moduels/2019-09-29-goproxy-cn/">干货满满的 Go Modules 和 goproxy.cn</a>
                                                    </div>
                                                </li>
                                                
                                            </ul>
                                        </div>
                                        
                                        
                                    </div>

                                    
                                    
                                    

                                    
                                    <div class="post-meta">
                                        
                                        
                                        <div class="post-tags">
                                            <ul>
                                            
                                            <li>
                                                <a href="/tags/go" title="View all posts tagged match">go</a>
                                            </li>
                                            
                                            </ul>
                                        </div>
                                        
                                        
                                        <nav class="further-reading">
                                            
                                            <div class="previous">
                                                <span>&lt;&lt; Prev</span>
                                                <a href="https://www.lican.asia/posts/posts/microservice/leaky-token-buckets/"
                                                    rel="prev">带你快速了解：限流中的漏桶和令牌桶算法</a> </div>
                                            
                                            
                                            <div class="next">
                                                <span>Next >></span>
                                                <a href="https://www.lican.asia/posts/posts/reading/programmer-accom-base/">必知必会！计算机里一些基本又重要的概念</a> 
                                            </div>
                                            
                                        </nav>
                                    </div>
                                    
                                    

                                    
                                    
                                    

                                </div>
                            </article>
                        </div>
                    </div>
                </section>
                <aside class="sidebar sidebar-primary" id="sidebar-primary" role="complementary">
    <h1 class="screen-reader-text">Sidebar</h1>
    

    
    
    <section id="text-2" class="widget widget_text">
        <div class="textwidget">
            
            <div id="profile">
                <div id="profile_picture"><img src="https://www.lican.asia/logo/logo.png"></div>
                <div id="profile_intro">
                    <p><span class="name">Lican</span></p>
                    <p class="intro">全栈开发者，爱好造轮子。</p>
                </div>
            </div>
            
            <p>
                <script type="text/javascript">
                    (function ($) {
                        $(document).ready(function () {
                            var menuPrimaryContainer = $('#menu-primary-container');
                            var profile = $('#text-2');
                            $('#toggle-navigation').click(function () {
                                if (menuPrimaryContainer.hasClass('open')) {
                                    profile.removeClass('open');
                                } else {
                                    profile.addClass('open');
                                }
                            });
                        });
                    })(jQuery);
                </script>
            </p>
        </div>
    </section>
    
    
    
    
    
    <section id="text-5" class="widget widget_text">
        <h2 class="widget-title">开源项目</h2>
        <div class="textwidget">
            <div id="projects" style="line-height: 22px;">
                
                <a href="https://github.com/idoubi/gonews"
                    target="_blank">gonews</a>: &nbsp;Daily news for golang<br>
                
                <a href="https://github.com/idoubi/sql2struct"
                    target="_blank">sql2struct</a>: &nbsp;Generate go struct according to SQL<br>
                
                <a href="https://github.com/idoubi/goz"
                    target="_blank">goz</a>: &nbsp;Request library used in golang<br>
                
        </div>
    </section>
    
    

    
    
    
    
    <section id="qrcode" class="widget widget_media_image">
        <h2 class="widget-title">微信公众号</h2>
        <img width="258" height="258"
            src="https://www.lican.asia/wechat/lican.png"
            class="image wp-image-5514  attachment-full size-full" alt=""
            style="max-width: 100%; height: auto;"
            sizes="(max-width: 258px) 100vw, 258px">
    </section>
    
    

    
    
    

    
    
    

    
    
    
    
</aside>
            </div>
        </div>

        <footer id="site-footer" class="site-footer" role="contentinfo">
    <div class="max-width">
    </div>
    <div class="footer">
        <div id="footercontent">
            © lican.asia All rights reserved<br/>
            Built with Hugo Theme <a href="https://github.com/idoubi/hugo-theme-period" target="_blank">Period</a>
        </div>
    </div>
</footer>

<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?e8351b6d4626d5881d439ea1f6184baa";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>
    
    
    </div>
    
</body>

</html>